# (c) Copyright 2008 Paul Hankin. All Rights Reserved. 

"""Functions for rendering backgammon boards"""

# Compact representation: O/X + one number per point.
def _man_compact(board, i):
    if board[i] == 0: return '-  '
    else: return '%s%-2d' % ('OX'[board[i] < 0], abs(board[i]))

def toString_compact(board, reverse_numbers=False):
    "A compact ascii representation of a board."
    board, nums = [None] * 4, [None] * 4
    rs = [xrange(*m) for m in ((1, 7), (7, 13), (18, 12, -1), (24, 18, -1))]
    for i, q in enumerate(rs):
        board[i] = ' '.join(_man_compact(board, x) for x in q)
        nums[i] = ' '.join('%-3d' % x for x in q)
    if reverse_numbers:
        nums.reverse()
    return '\n'.join('%s | %s | %s' % (b0, bar, b1) for
        b0, b1, bar in [
            (nums[0], nums[1], 'BAR'),
            (board[0], board[1], _man_compact(board, 0)),
            (' ' * 23, ' ' * 23, '   '),
            (board[3], board[2], _man_compact(board, 25)),
            (nums[3], nums[2], 'BAR')
            ])

# Ascii board
def _man_ascii(board, i):
    bar = (i == 0 or i == 25)
    dot = '. ' if not bar else '  '
    piece, count = ['O ', 'X '][board[i] < 0], abs(board[i])
    result = [piece] * min(5, count) + [dot] * max(0, 5 - count)
    # Replace top man with a number if there's too many men to fit.
    if count > 5: result[4] = '%-2d' % count
    # Bar men drawn upside down - first man appears near centre of board
    if bar: result = [' ' + x for x in reversed(result)]
    assert len(result) == 5, result
    return result

def toString_asciig(board, reverse_numbers=False):
    tables, nums = [None] * 4, [None] * 4
    rs = [xrange(*m) for m in ((1, 7), (7, 13), (18, 12, -1), (24, 18, -1))]
    for i, q in enumerate(rs):
        tables[i] = [' '.join(a) for a in zip(*(_man_ascii(board, x) for x in q))]
        nums[i] = ' '.join('%-2d' % x for x in q)
    if reverse_numbers:
        nums.reverse()
    yield nums[0], 'BAR', nums[1]
    for i in xrange(5):
        yield tables[0][i], _man_ascii(board, 0)[i], tables[1][i]
    yield ' ' * 17, '   ', ' ' * 17
    for i in xrange(4, -1, -1):
        yield tables[3][i], _man_ascii(board, 25)[i], tables[2][i]
    yield nums[3], 'BAR', nums[2]

def toString_ascii(board, reverse_numbers=False):
    "An ascii board."
    return '\n'.join('%s | %s | %s' % x for x in toString_asciig(board, reverse_numbers))
    
# HTML representation
def _html_man(board, i):
    if not board[i]: return '0'
    elif board[i] > 0: return 'O_%d' % board[i]
    else: return 'X_%d' % -board[i]

def _html_points(board, cube, up, player, range1, range2, NEWLINE):            
    OX, bar = 'OX'[player], (0, 25)[player]
    for a, this_range in [
            ('out_%s_%d' % (OX, board.men_off[player]), range1),
            ('bar_%s_%d' % (up, min(4, abs(board[bar]))), range2)]:
        yield a, 25, 66
        for i in this_range:
            tone = ('light', 'dark')[i % 2]
            man = _html_man(board, i)
            yield 'point_%s_%s_%s' % (up, tone, man), 18, 66
    cv = min(cube[1], 32) if cube[1] > 1 and cube[0] == player else 0
    yield 'cube_%s_%d' % (up, cv), 25, 66
    yield NEWLINE
    
def toString_htmlg(board, reverse_numbers=False, **kwargs):
    NEWLINE = 'br', 0, 0
    player = kwargs.get('player', 0)
    cube = kwargs.get('cube', None) or (None, 0)
    offer = kwargs.get('offer', None)
    num1, num2 = 'numbers_0112', 'numbers_2413'
    if reverse_numbers: num1, num2 = num2, num1
    # Disable side cube if we're displaying it on the board.
    if offer: cube = (None, 0)
    # Top line of point numbers
    yield num1, 291, 18
    yield NEWLINE
    # Points 1 to 12
    for x in _html_points(board, cube, 'up', 0, xrange(1, 7), xrange(7, 13), NEWLINE):
        yield x
    # The middle of the board - dice and cubes
    yield 'center_left', 48, 20
    for n in (0, 1):
        dice = kwargs.get('dice', None) or (0, 0)
        ds = ['dice-%s-%d' % ('OX'[n], player == n and dice[i]) for i in [0, 1]]
        cv = offer[1] if offer and offer[1] > 1 and offer[0] == n else 0
        # Left-hand die
        yield ds[0], 22, 20
        # Cube in centre of table if it's being offered
        if cv: yield 'cube_%d' % offer[1], 22, 20
        # Right-hand die
        yield ds[1], 22, 20
        # Blank space for cube
        if not cv: yield 'cube_0', 22, 20
        # Centre-square after left-hand side
        if n == 0: yield 'center_center', 63, 20
    yield 'center_right', 24, 20
    # Draw three possible cubes on the right-hand side of the board.
    # ... dotted cube for crawford.
    if kwargs.get('crawford', False): yield 'cube_empty', 24, 20
    # ... centred cube if noone's doubled.
    elif cube == (None, 1): yield 'cube_center', 24, 20
    # ... blank space if the cube is offered or owned by someone.
    else: yield 'cube_blank', 24, 20
    yield NEWLINE
    # Points 24 to 13.
    for x in _html_points(board, cube, 'down', 1, xrange(24, 18, -1), xrange(18, 12, -1), NEWLINE):
        yield x
    # The bottom line of point numbers
    yield num2, 291, 18
    
# toString_htmlg generates (imagename, width and height) or ('br', 0, 0).
# In the latter case, we have to produce a <BR> in the html, before the next
# <IMG> tag.
def toString_htmlg2(board, reverse_numbers=False, **kwargs):
    "Generate lines of html representing board."
    image_path = kwargs.get('image_path', 'images/')
    written, line = False, ''
    for img, w, h in toString_htmlg(board, reverse_numbers, **kwargs):
        if img == 'br':
            yield line + '<BR><IMG'
            written = True
        else:
            if not written: yield line + '<IMG'
            line = 'SRC="%(image_path)s%(img)s.gif" WIDTH=%(w)d HEIGHT=%(h)d>' % locals()
            written = False
    else:
        yield line
        
def toString_html(board, reverse_numbers=False, *args, **kwargs):
    "HTML representation of a board."
    return '\n'.join(toString_htmlg2(board, reverse_numbers, *args, **kwargs))
